namespace StreetCrime
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Models;
    using Serialization;
    using WP7Contrib.Collections;
    using WP7Contrib.Common;

    [Serializer(typeof(StreetLevelCrimeResult))]
    public class StreetLevelCrimeResult : BaseModel, ISerializeObject
    {
        private readonly ObservableCollection<StreetLevelCrime> crimes;

        public StreetLevelCrimeResult()
        {
            this.crimes = new ObservableCollection<StreetLevelCrime>();
        }

        public object[] Serialize(object target)
        {
            var crimeResult = (StreetLevelCrimeResult)target;
            return new object[] { 
                                    crimeResult.Crimes.ToList()
                                };
        }

        public object Deserialize(object[] data)
        {
            var crimeResult = new StreetLevelCrimeResult();
            crimeResult.AddCrimes((List<StreetLevelCrime>)data[0]);

            return crimeResult;
        }

        public ReadOnlyObservableCollection<StreetLevelCrime> Crimes
        {
            get
            {
                return new ReadOnlyObservableCollection<StreetLevelCrime>(this.crimes);
            }
        }

        public StreetLevelCrimeResult AddCrime(StreetLevelCrime location)
        {
            this.crimes.Add(location);

            return this;
        }

        public StreetLevelCrimeResult AddCrimes(IEnumerable<StreetLevelCrime> crimeArray)
        {
            crimeArray.ToList().ForEach(c => this.crimes.Add(c));

            return this;
        }

        public IEnumerable<StreetLevelCrimeCategoryGrouping> GroupByCategory()
        {
            var groupedCrimes = this.crimes
                .OrderBy(c => c.Category)
                .GroupBy(c => c.Category)
                .Select(cc => new StreetLevelCrimeCategoryGrouping(cc.First().Category, cc.ToList())).ToList();

            return groupedCrimes;
        }

        public IEnumerable<StreetLevelCrimeStreetGrouping> GroupByStreet()
        {
            var groupedCrimes = this.crimes
                .OrderBy(c => c.Location.Street.Name)
                .ThenBy(c => c.Category)
                .GroupBy(c => c.Location.Street.Name)
                .Select(cc => new StreetLevelCrimeStreetGrouping(cc.First().Location.Street, cc.ToList()))
                .ToList();

            return groupedCrimes;
        }

        public IEnumerable<StreetLevelCrimeStreetAndCategoryGrouping> GroupByStreetAndCategory()
        {
            var groupedCrimes = this.crimes
                .OrderBy(c => c.Location.Street.Name)
                .GroupBy(c => c.Location.Street.Name)
                .Select(cs => new StreetLevelCrimeStreetAndCategoryGrouping(cs.First().Location.Street,
                    cs.OrderBy(c => c.Category)
                    .GroupBy(c => c.Category)
                    .Select(cc => new StreetLevelCrimeCategoryGrouping(cc.First().Category, cc.ToList())).ToList()))
                .ToList();

            return groupedCrimes;
        }

        public IEnumerable<StreetLevelCrimeStreetGrouping> FilterByGroup(StreetLevelCrimeCategory category)
        {
            var groupedCrimes = this.crimes
                .Where(c => c.Category == category)
                .GroupBy(c => c.Location.Street.Name)
                .Select(cc => new StreetLevelCrimeStreetGrouping(cc.First().Location.Street, cc.ToList()))
                .ToList();

            return groupedCrimes;
        }

        public IEnumerable<StreetLevelCrimeCategoryGrouping> FilterByStreet(string street)
        {
            var groupedCrimes = this.crimes
                .Where(c => string.Compare(c.Location.Street.Name, street, StringComparison.InvariantCultureIgnoreCase) == 0)
                .GroupBy(c => c.Category)
                .Select(cc => new StreetLevelCrimeCategoryGrouping(cc.First().Category, cc.ToList()))
                .ToList();

            return groupedCrimes;
        }
    }
}